Visualizaciones

Ggplot2

Vamos a empezar a trabajar con visualizaciones y maneras de graficar en R. Uno de los paquetes de visualizaciones más extendidos en el lenguaje es ggplot2. Efectivamente, adivinaron, es parte de tidyverse. Este lenguaje tiene una sintaxis propia. El mismo funciona con capas, que vamos a ir viendo. Basicamente:

  • Con ggplot() definimos los datos y variables que vamos a utilizar en los ejes del gráfico, como también color, forma, y otros parámetros básicos.

  • Con + incorporamos otras capas al gráfico.

  • Hay diferentes capas:

  • Tipos de gráficos (geom_points(), geom_bar(), geom_boxplot(), entre otros)
  • Títulos (labs())
  • Estilo (theme())
  • Escalas (scale_y_continuous() por ejemplo)
  • División en diferentes gráficos (facet_wrap(), `facet_grid())
  • Animación (transition_revel())

Hay muchísimas funciones. Las podemos chequear en el cheatsheet.

Para empezar, primero cargamos la librería con tidyverse.

#install.packages("tidyverse")
library(tidyverse)
## -- Attaching packages ----------------------------------------------- tidyverse 1.2.1 --
## v ggplot2 3.2.1     v purrr   0.3.2
## v tibble  2.1.3     v dplyr   0.8.3
## v tidyr   0.8.3     v stringr 1.4.0
## v readr   1.3.1     v forcats 0.4.0
## -- Conflicts -------------------------------------------------- tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()

Seteamos algunos parámetros generales, cargamos la data que vamos a usar (un set de datos que viene predeterminado con la librería).

###Setup
#install.packages("knitr")
#install.packages("kableExtra")
library(knitr)
library(kableExtra)
## 
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
## 
##     group_rows
options(scipen=999)  # apagamos la notación científica
data("midwest", package = "ggplot2")  # cargamos los datos
# midwest <- read.csv("http://goo.gl/G1K41K") # Otra forma de cargar los datos
# Iniciamos Ggplot, sólo va a tener los ejes (columnas del dataframe)
kable(head(midwest))
PID county state area poptotal popdensity popwhite popblack popamerindian popasian popother percwhite percblack percamerindan percasian percother popadults perchsd percollege percprof poppovertyknown percpovertyknown percbelowpoverty percchildbelowpovert percadultpoverty percelderlypoverty inmetro category
561 ADAMS IL 0.052 66090 1270.9615 63917 1702 98 249 124 96.71206 2.5752761 0.1482826 0.3767590 0.1876229 43298 75.10740 19.63139 4.355859 63628 96.27478 13.151443 18.01172 11.009776 12.443812 0 AAR
562 ALEXANDER IL 0.014 10626 759.0000 7054 3496 19 48 9 66.38434 32.9004329 0.1788067 0.4517222 0.0846979 6724 59.72635 11.24331 2.870315 10529 99.08714 32.244278 45.82651 27.385647 25.228976 0 LHR
563 BOND IL 0.022 14991 681.4091 14477 429 35 16 34 96.57128 2.8617170 0.2334734 0.1067307 0.2268028 9669 69.33499 17.03382 4.488572 14235 94.95697 12.068844 14.03606 10.852090 12.697410 0 AAR
564 BOONE IL 0.017 30806 1812.1176 29344 127 46 150 1139 95.25417 0.4122574 0.1493216 0.4869181 3.6973317 19272 75.47219 17.27895 4.197800 30337 98.47757 7.209019 11.17954 5.536013 6.217047 1 ALU
565 BROWN IL 0.018 5836 324.2222 5264 547 14 5 6 90.19877 9.3728581 0.2398903 0.0856751 0.1028101 3979 68.86152 14.47600 3.367680 4815 82.50514 13.520249 13.02289 11.143211 19.200000 0 AAR
566 BUREAU IL 0.050 35688 713.7600 35157 50 65 195 221 98.51210 0.1401031 0.1821340 0.5464022 0.6192558 23444 76.62941 18.90462 3.275892 35107 98.37200 10.399635 14.15882 8.179287 11.008586 0 AAR
ggplot(midwest, aes(x=area, y=poptotal))

Scatterplot

Vamos a agrega un simple scatterplot sumando la función geom_point()

###Setup
ggplot(midwest, aes(x=area, y=poptotal)) + geom_point()

Al igual que geom_point() hay muchas capas “geom” que podemos ir adicionando para completar o complementar el gráfico. Vamos a agregar una capa suavizada con la función geom_smooth(method='lm')

g <- ggplot(midwest, aes(x=area, y=poptotal)) + geom_point() + geom_smooth(method="lm")  # con se=FALSE podemos remover los intervalos de confianza
#los objeto s de ggplot los podemos guardar en un objeto y luego ejecutarlos con la función base de R "plot()""
plot(g)

#buscando en la ayuda podemos ver otros métodos de geom_smooth()
#descomentar y ejecutar!
#?geom_smooth

Podemos ajustar los límites de los ejes X e Y. Podemos usar dos métodos: Eliminando los valores por fuera del rango o zoomeando.

El primer método puede ser realizado con xlim() y ylim(), pasando un vector delimitando los valores. Al eliminar los valores, la recta dibujada por el modelo lineal modifica su pendiente.

g <- ggplot(midwest, aes(x=area, y=poptotal)) + geom_point() + geom_smooth(method="lm")  
# Eliminamos los puntos por fuera del límite
g + xlim(c(0, 0.1)) + ylim(c(0, 1000000))   # deletes points
## Warning: Removed 5 rows containing non-finite values (stat_smooth).
## Warning: Removed 5 rows containing missing values (geom_point).

El otro método es zoomear sin eliminar los puntos con coord_cartesian()

#Aquí la recta del modelo lineal no cambia porque no eliminamos valores, solamente no los estamos mostrando porque zoomeamos en el rango indicado
g <- ggplot(midwest, aes(x=area, y=poptotal)) + geom_point() + geom_smooth(method="lm") 
g1 <- g + coord_cartesian(xlim=c(0,0.1), ylim=c(0, 1000000))  # zooms in
plot(g1)

Podemos sumar títulos y etiquetas para los ejes X e Y. Se puede hacer con labs() con parámetros title, x y y, aunque también se puede usar ggtitle(), xlab() y ylab().

g <- ggplot(midwest, aes(x=area, y=poptotal)) + geom_point() + geom_smooth(method="lm") 

g1 <- g + coord_cartesian(xlim=c(0,0.1), ylim=c(0, 1000000))  

# Sumo títulos o etiquetas
g1 + labs(title="Area Vs Population", subtitle="From midwest dataset", y="Population", x="Area", caption="Midwest Demographics")

# Opción 2

g1 + ggtitle("Area Vs Population", subtitle="From midwest dataset") + xlab("Area") + ylab("Population")

# Puedo hacer el gráfico todo junto
ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point() + 
  geom_smooth(method="lm") + 
  coord_cartesian(xlim=c(0,0.1), ylim=c(0, 1000000)) + 
  labs(title="Area Vs Población", subtitle="Dataset Midwest", y="Población", x="Area", caption="Datos demográficos de Midwest")

Puedo cambiar el color y el tamaño de la capa “geom” de forma estática, indicando el valor de forma/color que queremos.

ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(col="steelblue", size=3) +   # Cambio color y tamaño de los puntos
  geom_smooth(method="lm", col="firebrick") +  # Cambio color de la línea
  coord_cartesian(xlim=c(0, 0.1), ylim=c(0, 1000000)) + 
  labs(title="Area Vs Población", subtitle="Dataset Midwest", y="Población", x="Area", caption="Datos demográficos de Midwest")

Respecto a los colores, los podemos indicar de diferentes formas: con valores fijos especificados con el nombre del color (recomendamos chequear acá, donde podemos ver funciones de color saturación, luminosidad, entre otros o acá); con valores fijos determinados en código HEX o usando paletas predefinidas. Para esto último, recomendamos la librería RColorBrewer, acá pueden ver la documentación. Igualmente, ggplot2 tiene paletas predefinidas (pero las van a ver en muchos lados, por lo cual está bueno modificarlas!)

Por otra parte, podemos indicar que los atributos visuales (color, shape, size, stroke, fill, alpha). Para esto, los tenemos que pasar dentro de la función aes()

gg <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state), size=3) +  # Acá seteamos el color a la variable "state"
  geom_smooth(method="lm", col="firebrick", size=2) + 
  coord_cartesian(xlim=c(0, 0.1), ylim=c(0, 1000000)) + 
  labs(title="Area Vs Población", subtitle="Dataset Midwest", y="Población", x="Area", caption="Datos demográficos de Midwest")
plot(gg)

#Podemos remover la leyenda sumando theme()
gg + theme(legend.position="None")

#O definir que el gráfico tome una escala en particular
gg + scale_colour_brewer(palette = "Set1")

#Como mencionamos, el paquete RColorBrewer trae muchas paletas copadas
#install.packages(RColorBrewer)
library(RColorBrewer)
kable(head(brewer.pal.info, 10)) 
maxcolors category colorblind
BrBG 11 div TRUE
PiYG 11 div TRUE
PRGn 11 div TRUE
PuOr 11 div TRUE
RdBu 11 div TRUE
RdGy 11 div FALSE
RdYlBu 11 div TRUE
RdYlGn 11 div FALSE
Spectral 11 div FALSE
Accent 8 qual FALSE

Podemos ver algunas de las paletas de RColorBrewer

También podemos cambiar los valores de la escala. Una forma es con el parámetro `breaks dentro de scale_x_continuous() (para valores continuos)

#Usamos el objeto gg que ya está definido
#A breaks le pasamos una sequencia de valores
gg + scale_x_continuous(breaks=seq(0, 0.1, 0.01))

#A estos breaks le podemos sumar etiquetas. El objeto letters tiene el abecedario guardado
gg + scale_x_continuous(breaks=seq(0, 0.1, 0.01), labels = letters[1:11])

#Se puede revertir la escala con la función scale_x_reverse()
gg + scale_x_reverse()

También podemos customizar los textos de los ejes.

gg + scale_x_continuous(breaks=seq(0, 0.1, 0.01), labels = sprintf("%1.2f%%", seq(0, 0.1, 0.01))) + 
  scale_y_continuous(breaks=seq(0, 1000000, 200000), labels = function(x){paste0(x/1000, 'K')})

También podemos cambiar los fondos del gráfico (“themes”), como ya vimos en R, de un par de maneras diferentes. Podemos encontrar muchos “themes”, paletas, geoms (capas de tipos de gráficos) y escalas en la librería ggthemes()

# Usando theme_set(), lo seteamos para el resto de los gráficos
theme_set(theme_classic())
gg

# Otra forma: Sumandolo como una capa más
gg + theme_bw() + labs(subtitle="BW Theme")

gg + theme_classic() + labs(subtitle="Classic Theme")

#Probamos un theme de ggthemes()
#install.packages("ggthemes")
library(ggthemes)
#usamos el theme de google docs!
gg + theme_gdocs() + ggtitle("Probando")+ scale_color_gdocs()

# O el de Wall Street Journal
gg + scale_colour_wsj('colors6', '') + theme_wsj()

También se pueden hacer gráficos 3D. Para esta funcionalidad, vamos a probar otra librería de gráficos que últimamente es muy utilizada también, llamada plotly, la cual se usa desde R, Python y otros lenguajes también. Chequeen la documentación para R de la librería. Una buena práctica con los gráficos en general es buscar una referencia primero. Las librerías como ggplot2, plotly cuentan con muchos ejemplos. Hay otras páginas data-viz que tienen muchos ejemplos en R con gráficos muy interesantes.

#seteamos una semilla, para poder reproducir resultados
set.seed(417)
library(plotly)
## 
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## The following object is masked from 'package:stats':
## 
##     filter
## The following object is masked from 'package:graphics':
## 
##     layout
#generamos 3 distribuciones
temp <- rnorm(100, mean=30, sd=5)
pressure <- rnorm(100)
dtime <- 1:100

plot_ly(x=temp, y=pressure, z=dtime, type="scatter3d", mode="markers", color=temp)

Hay formas de separar los gráficos separadamente según una categoría. Esto lo hacemos con facet_wrap(). Por otra parte, para la superposición de puntos, podemos setear el parámetro alpha para graficar la intensidad de la superposición de puntos.

#Usamos el clásico Iris, que viene precargado en R 
#Antes, volvemos a setear el "theme" al que veníamos usando.
theme_set(theme_bw())
ggplot(data = iris, aes(x = Petal.Length, Petal.Width, color = Species))+
  geom_point(alpha=0.75)+
  facet_wrap(~Species) 

Gráficos de barras

Vamos a ver cómo hacer gráficos de barras con ggplot2. Obviamente, volvemos a usar radares.

radares <- read.csv("http://cdn.buenosaires.gob.ar/datosabiertos/datasets/flujo-vehicular-por-radares-ausa/flujo-vehicular-por-radares-2019.csv", header=TRUE, sep=',', dec='.')

agrupado <- radares %>% group_by(autopista_nombre)  %>% 
summarise(promedio_autopista = mean(cantidad))

kable(head(agrupado))
autopista_nombre promedio_autopista
AU 4 Lugones 4360.658
AU 5 Cantilo 3514.799
AU 9 de Julio Sur 2469.391
AU Dellepiane 2043.230
ggplot(data = agrupado, aes(x = autopista_nombre, promedio_autopista, fill = autopista_nombre))+
  geom_col(alpha=0.75)+
  labs(title = "Promedio por hora de paso de vehículos por autopistas")+
  theme(legend.position = 'none')

Podemos hacer una barra agrupada usando otra variable para subdividir una clase. Acá usamos un color para rellenar las barras en base a una variable, es decir de forma dinámica (a diferencia de la asignación de colores estáticos que vimos antes). Para eso, tenemos que incluir los parámetros visuales dentro de la función aes() dentro del geom a utilizar.

agrupado_con_sentido <- radares %>% group_by(autopista_nombre, seccion_sentido)  %>% 
summarise(cantidad_autopista_sentido = n())

kable(head(agrupado_con_sentido))
autopista_nombre seccion_sentido cantidad_autopista_sentido
AU 4 Lugones A 29363
AU 5 Cantilo B 16514
AU 9 de Julio Sur A 23990
AU 9 de Julio Sur B 28454
AU Dellepiane A 19156
AU Dellepiane B 23578
plotcito <- ggplot(data = agrupado_con_sentido, aes(x = autopista_nombre, y=cantidad_autopista_sentido, fill=seccion_sentido))+ geom_bar(stat='identity') +
  labs(title = "Cantidad de paso de vehículos por autopistas y dispositivo") + geom_text(label=agrupado_con_sentido$cantidad_autopista_sentido, position=position_stack(vjust=0.5), colour="white")
plot(plotcito)

#con esta función chequeamos la paleta actual
palette()
## [1] "black"   "red"     "green3"  "blue"    "cyan"    "magenta" "yellow" 
## [8] "gray"
#con coord_flip() invertimos las coordenadas
plotcito + coord_flip()

Boxplots

Este tipo de gráfico representan la distribución de una variable. Las cajas están delimitadas por un límite inferior que representa el 25% de la distribución y un límite superior que representa el 75% de la distribución. El gráfico marca los outliers, son los datos que se alejan de los límites de la distribución por las de 1,5 del rango intercuartil (es decir la extensión de esa “caja”).

ggplot(radares, aes(x = autopista_nombre, y = cantidad, fill = autopista_nombre)) +
  geom_boxplot()

Guardar los plots

Vamos a ver como guardar los plots con la función de ggplot2 ggsave(). Aloja varios formatos, como .jpg, .pdf, .png, entre otros.

graficopaguardar <- ggplot(radares, aes(x = autopista_nombre, y = cantidad, fill = autopista_nombre)) +
  geom_boxplot()

#Le ponemos el nombre de archivo a guardar (podríamos sumar el path/directorio que queremos) y por default guarda el último plot mostrado, pero esto se puede cambiar width, height y units marcan el tamaño de la imagen (sino se indican toman los default), y dpi marca la calidad con la que se guarda. 
ggsave("grafico.png", width = 20, height = 20, units = "cm", dpi=300)

gganimate()

Con esta librería, que es una extensión de ggplot2, podemos darle animación a nuestros gráficos. Vamos a ver un ejemplo.

#install.packages("gganimate")
library(gganimate)
library(data.table)
## 
## Attaching package: 'data.table'
## The following objects are masked from 'package:dplyr':
## 
##     between, first, last
## The following object is masked from 'package:purrr':
## 
##     transpose
sube <- fread("C:/Users/rama_/Desktop/Ramiro/hackaton/hackaton_sube/ext_dataton_v1.csv")

#ver código original: http://rpubs.com/martinalalu/movilidad-urbana

sube <- sube %>% 
  mutate(DIA=case_when(grepl(1,DIA_SEMANA)~"LUNES",
                      grepl(2,DIA_SEMANA)~"MARTES",
                      grepl(3,DIA_SEMANA)~"MIÉRCOLES",
                      grepl(4,DIA_SEMANA)~"JUEVES",
                      grepl(5,DIA_SEMANA)~"VIERNES",
                      grepl(6,DIA_SEMANA)~"SÁBADO",
                      grepl(7,DIA_SEMANA)~"DOMINGO"))

#Transacciones por día y modo
sube_dia_modo <-  sube %>%
  filter(MES==201711) %>% 
  group_by(MODO,DIA,DIA_SEMANA) %>% 
  summarise(total = sum(NTRX_PROM))


#Transacciones totales por dia
sube_dia <- sube_dia_modo %>% 
  group_by(DIA_SEMANA,DIA) %>% 
  summarise(total_dia=sum(total))

sube_dia <- sube_dia %>%  
  mutate(pct_dia=(total_dia*100)/sum(sube_dia$total_dia))

#Nos quedamos solo con las transacciones de la semana
sube_dia_hora_modo <- sube %>% 
  group_by(MODO,HORA,DIA,MES,DIA_SEMANA) %>% 
  summarise(total = sum(NTRX_PROM))

#Hacemos promedio de transacciones para días de la semana
sube_dia_hora_modo_2 <-  sube_dia_hora_modo %>% 
  group_by(MODO,HORA,DIA,DIA_SEMANA) %>% 
  summarise(total = mean(total)) %>% 
  arrange(DIA_SEMANA)

#Creo variables nuevas para los titulos del gráfico
dia.labs <- c("LUNES", "MARTES","MIÉRCOLES","JUEVES","VIERNES","SÁBADO","DOMINGO")
names(dia.labs) <- c("1", "2","3","4","5","6","7")

sube_hora_gif <- ggplot(data=sube_dia_hora_modo_2,aes(HORA,total,color=MODO)) + 
  geom_line()+
  facet_wrap(~DIA_SEMANA,labeller = labeller(DIA_SEMANA = dia.labs))+
  labs(title = "Distribución de transacciones SUBE por día, hora y modo",
         subtitle = "Ciudad Autónoma de Buenos Aires - 2018",
         x = "Hora",
         y = "Cantidad transacciones")+
  theme_minimal()+
  transition_reveal(HORA)

#install.packages("gifski")
library(gifski)
anim_save("sube_hora.gif", sube_hora_gif)
sube_hora_gif

## ggally

#install.packages("GGally")
#install.packages("ggridges")
library(GGally)
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2
## 
## Attaching package: 'GGally'
## The following object is masked from 'package:dplyr':
## 
##     nasa
library(ggridges)
## 
## Attaching package: 'ggridges'
## The following object is masked from 'package:ggplot2':
## 
##     scale_discrete_manual
ggpairs(iris,  mapping = aes(color = Species))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(iris, aes(x = Sepal.Length, y = Species, fill=Species)) + 
  geom_density_ridges()
## Picking joint bandwidth of 0.181

ggridges

Otra extensión muy buena de ggplot2 que nos permite graficar de manera muy descriptiva distribuciones es ggridges(). Chequeen su documentación

Para ayudarnos a armar código de ggplot podemos usar esquisse.

Ggvis

Otro paquete muy bueno de visualizaciones es ggvis(). Vean la documentación acá. Tiene otra sintaxis aunque es similar a la de ggplot2, ya que toma elementos de ésta, y los combina con shiny

Básicos de R

Hasta ahora vimos gráficos realizados con funciones de varios paquetes (ggplot2, plotly, ggvis). R trae algunas funciones predeterminadas para plotear, que nos pueden ser de utilidad para realizar plots rápidas. Para esto, podemos usar las funciones plot(), barplot(), hist(), entre otros. Vamos a ver algunos de estos usos.

#scatterplot
plot(iris$Sepal.Width, iris$Sepal.Length)

#Puede ser un poco más bello, aunque no a niveles de los gráficos que vimos
plot(iris$Sepal.Length, iris$Petal.Length,
     col = iris$Species,
     pch = 16,
     cex = 2)
legend(x = 4.5, y = 7, legend = levels(iris$Species), col = c(1:3), pch = 16)

#histograma
hist(iris$Petal.Length)

#scatterplot matrix
pairs(iris)

#boxplot
boxplot(iris$Sepal.Length ~ iris$Species, notch = T)